﻿/*************************************************************************************
 * 文 件 名:   HK_HierarchyPro
 * 
 * 描    述:   Hierarchy视窗的拓展，可以在Setting里面关闭或开启
 * 
 * 版    本：  V1.0
 * 创 建 者：  京产肠饭
 * 创建时间：  2021/11/29
 * 链   接：  https://gitee.com/JingChanChangFan/hk_-unity-tools
*************************************************************************************/

using System.Collections.Generic;
using System.Reflection;
using UnityEngine;
using UnityEditor;

namespace HKTools
{
    [InitializeOnLoad]
    public class HK_HierarchyPro
    {
        public static void ChangeState(bool isActive)
        {
            if (isActive == true)
            {
                EditorApplication.hierarchyWindowItemOnGUI += ComponentsShow;
                EditorApplication.hierarchyWindowItemOnGUI += TagAndLayerShow;
                EditorApplication.hierarchyWindowItemOnGUI += ToggleShow;
            }
            else
            {
                EditorApplication.hierarchyWindowItemOnGUI -= ComponentsShow;
                EditorApplication.hierarchyWindowItemOnGUI -= TagAndLayerShow;
                EditorApplication.hierarchyWindowItemOnGUI -= ToggleShow;
            }
        }

        /*最大显示的图标数量*/
        static int maxIconNum = 3;

        /*当前需要便宜 组件 显示的 物体TF*/
        static Transform nowOffsetTF = null;
        static int offsetNum = 0;

        // 想要显示的 组件 列表
        static List<System.Type> showTypes = new List<System.Type>(){
        typeof(Camera),
        typeof(Collider),
        typeof(AudioSource),
        typeof(AudioListener),
    };

        // 显示物体的 tag 和 layer 
        static void TagAndLayerShow(int instanceid, Rect rect)
        {
            GameObject go = EditorUtility.InstanceIDToObject(instanceid) as GameObject;

            //剔除最上面的scene 物体
            if (!go)
                return;

            Rect newRect = new Rect(rect);

            if (rect.width > 280)
            {
                newRect.x = newRect.width + newRect.x - 120;
                newRect.y += 2;
                var myStyle = new GUIStyle();
                myStyle.normal.textColor = Color.gray;

                string layerStr = LayerMask.LayerToName(go.layer);

                if (layerStr != "Default")
                {
                    GUI.Label(newRect, '[' + layerStr + ']', myStyle);
                }
            }
        }

        // 显示最右侧的激活按钮
        static void ToggleShow(int instanceid, Rect rect)
        {
            GameObject go = EditorUtility.InstanceIDToObject(instanceid) as GameObject;
            //剔除最上面的scene 物体
            if (!go)
            {
                return;
            }

            /*----------------------------Toggle 显示----------------------*/

            Rect newRect = new Rect(rect);
            newRect.x = newRect.width + newRect.x - 15;
            newRect.y += 2;

            GUI.changed = false;
            GUI.Toggle(newRect, go.activeSelf, "");

            if (GUI.changed)
                go.SetActive(!go.activeSelf);

            /*----------------------------Toggle 显示----------------------*/
        }

        // 显示 特殊的组件列表
        static void ComponentsShow(int instanceid, Rect rect)
        {
            GameObject go = EditorUtility.InstanceIDToObject(instanceid) as GameObject;

            //剔除最上面的scene 物体
            if (!go)
                return;

            //求出当前Hierarchy的总宽度
            rect.width += rect.x;
            //取得当前物体所有的 Component
            List<Component> coms = new List<Component>(go.GetComponents<Component>());
            //遍历coms中的组件类型，如果有不显示的，就移除掉
            for (int i = 0; i < coms.Count; i++)
            {
                if (!coms[i])
                {
                    continue;
                }
                //剔除不需要显示的 组件
                if (TypeCheck(coms[i].GetType()))
                {
                    coms.RemoveAt(i);
                    i--;
                }
            }
            //图标的大小
            int iconSize = 16;

            //如果当前物体是 需要偏移的物体  offset 为 offsetNum 否则为 0
            int offset = go.transform == nowOffsetTF ? offset = offsetNum : 0;
            //如果当前物体的组件数量 <=  maxIconNum  把nowOffset 和 offsetNum 
            if (coms.Count <= maxIconNum && go.transform == nowOffsetTF)
            {
                nowOffsetTF = null;
                offsetNum = 0;
            }
            if (coms.Count > maxIconNum)
            {
                GUIStyle style = new GUIStyle(GUI.skin.label);
                style.fontSize = 9;
                style.alignment = TextAnchor.MiddleCenter;

                if (GUI.Button(new Rect(rect.width - (iconSize + 1.5f) * (maxIconNum + 2), rect.y + 1, 17, 17), "•••", style))
                {
                    if (nowOffsetTF != go.transform)
                    {
                        nowOffsetTF = go.transform;
                        offsetNum = 0;
                    }
                    offsetNum += maxIconNum;
                    if (offsetNum >= coms.Count)
                    {
                        offsetNum = 0;
                    }
                }
            }

            for (int i = 0; i + offset < coms.Count && i < maxIconNum; i++)
            {
                Component com = coms[i + offset];
                PropertyInfo propertyInfo;
                try
                {
                    propertyInfo = com.GetType().GetProperty("enabled");
                }
                catch
                {
                    continue;
                }

                if ((bool)propertyInfo.GetGetMethod().Invoke(com, null))
                {
                    GUI.color = Color.white;
                }
                else
                {
                    GUI.color = Color.gray;
                }

                //根据组件类型返回 组件的小图标
                Texture2D comTexture = AssetPreview.GetMiniThumbnail(com);
                if (comTexture)
                {
                    GUIStyle style = new GUIStyle();
                    style.normal.textColor = Color.white;

                    if (GUI.Button(new Rect(rect.width - (iconSize + 1) * (i + 2), rect.y + 1, iconSize, iconSize), comTexture, style))
                    {
                        bool enabled = (bool)propertyInfo.GetGetMethod().Invoke(com, null);

                        Undo.RecordObject(com, enabled ? "Disable Component" : "Enable Component");
                        propertyInfo.GetSetMethod().Invoke(com, new object[] { !enabled });
                    }
                    GUI.color = Color.white;
                }
            }
        }

        // 检查当前组件类型 是否是 不显示类型
        static bool TypeCheck(System.Type type)
        {
            for (int i = 0; i < showTypes.Count; i++)
            {
                if (type == showTypes[i] || type.IsSubclassOf(showTypes[i]))
                {
                    return false;
                }
            }
            return true;
        }
    }
}